package server;

import java.util.*;

import common.*;
import common.CMessage.MsgType;
import common.CMessage29.GameState29;
import common.CMessage29.NextJobTypes;

public class C29Server extends C4PlayerGameServer {
	private GameState29 e_gamestate;
	private class CDeck29{
		private LinkedList<CCard29> list_cardsCurrent; public CCard29 GetNextCard(){ return list_cardsCurrent.poll();}
		private Random rand;
		//private LinkedList<LinkedList<CCard29>> lists_cardsOld; 
//		private int i_idLastWinner;
//		public void StoreWinningHand(CCard29[] arr_fourcards, int id_winner) 
//		{
////			i_idLastWinner = id_winner;
//			for(int i=0; i<4;i++)
//				lists_cardsOld.get(id_winner).add(arr_fourcards[i]);
//		} 
		
//		public CCard29[] GetLastWinningHand(){
//				CCard29[] arr_lastwinhand;
//				arr_lastwinhand = new CCard29[4];
//				for(int i=0; i<4;i++)
//					arr_lastwinhand[i] = lists_cardsOld.get(i_idLastWinner).get(index)
//		}
		public CDeck29(){
			list_cardsCurrent = new LinkedList<CCard29>();
			//lists_cardsOld = new LinkedList< LinkedList< CCard29 >>();
			//for(int i=0; i<4; i++)
			//	lists_cardsOld.add(new LinkedList<CCard29>());

			for(int i=0; i<4;i++){
				list_cardsCurrent.add(new CCard29(i,1));
				for(int j=7; j<14;j++)
					list_cardsCurrent.add(new CCard29(i,j));
			}		
			Collections.shuffle(list_cardsCurrent);
			rand = new Random();
//			i_idLastWinner = -1;
		}
		
		public void BiasedShuffle(){
			int iCutCnt = rand.nextInt(5);
			iCutCnt +=1;
			for(int i = 0; i<iCutCnt; i++){
				int iCutPt = rand.nextInt(32);
				for(int j=0; j<iCutPt; j++){
					CCard29 temp = list_cardsCurrent.poll();
					list_cardsCurrent.addLast(temp);
				}				
			}
		}
		

	}
	private class CBid29{
		public boolean b_IsBiddingDone;
		public int i_currentWinningBid;
		public int i_Seat_CurrentWinner;
		public int i_Seat_CurrentBidder;
		public int i_Seat_InitPlayer;
		public int i_MinNextBid;
		
		public CBid29(int i_SeatInitPlayer){
			b_IsBiddingDone = false;
			i_currentWinningBid = 15;
			i_MinNextBid = 16;
			i_Seat_CurrentWinner = -1;
			i_Seat_CurrentBidder = i_SeatInitPlayer;
			this.i_Seat_InitPlayer = i_SeatInitPlayer;
		}
		
		public boolean BidReceived(int i_SenderSeat, int i_Bid){
			if(b_IsBiddingDone)
				return false;
			if(i_SenderSeat!=i_Seat_CurrentBidder)
				return false;
			if(i_Bid==29){
				b_IsBiddingDone = true;
				i_Seat_CurrentWinner = i_SenderSeat;
				i_currentWinningBid = 29;
				return true;
			}
			if(i_Seat_CurrentWinner == -1){
				if(i_Bid == 0){
					i_Seat_CurrentBidder = (i_Seat_CurrentBidder + 1)%4;
				}
				else if(i_Bid > 15 && i_Bid < 30){
					//Equivalently this condition could have been else if(i_Bid >= i_MinNextBid && i_Bid < 30)
					i_Seat_CurrentWinner = i_Seat_CurrentBidder;
					i_Seat_CurrentBidder = (i_Seat_CurrentBidder + 1)%4;
					i_currentWinningBid = i_Bid;
					i_MinNextBid = i_currentWinningBid + 1;
					if((i_Seat_CurrentBidder==i_Seat_InitPlayer)){
						b_IsBiddingDone = true;
						i_MinNextBid = -1;
						return true;
					}
					
				}
				else
					return false;									
			}
			else{
				if((i_Seat_CurrentBidder-i_Seat_InitPlayer + 4)%4 > (i_Seat_CurrentWinner-i_Seat_InitPlayer + 4)%4){
					if(i_Bid == 0){
						i_Seat_CurrentBidder = (i_Seat_CurrentBidder + 1)%4;
					}
					else if(i_Bid > i_currentWinningBid && i_Bid < 30){ 
						//Equivalently this condition could have been else if(i_Bid >= i_MinNextBid && i_Bid < 30)
						int temp = i_Seat_CurrentWinner;
						i_Seat_CurrentWinner = i_Seat_CurrentBidder;
						i_Seat_CurrentBidder = temp;
						i_currentWinningBid = i_Bid;
						i_MinNextBid = i_currentWinningBid;
					}
					else
						return false;	
				}
				else{
					if(i_Bid == 0){
						i_Seat_CurrentBidder = (i_Seat_CurrentWinner + 1)%4;
						i_MinNextBid = i_currentWinningBid + 1;
					}
					else if(i_Bid >= i_currentWinningBid && i_Bid < 30){ 
						//Equivalently this condition could have been else if(i_Bid >= i_MinNextBid && i_Bid < 30)
						int temp = i_Seat_CurrentWinner;
						i_Seat_CurrentWinner = i_Seat_CurrentBidder;
						i_Seat_CurrentBidder = temp;
						i_currentWinningBid = i_Bid;
						i_MinNextBid = i_currentWinningBid + 1;
					}
					else
						return false;	
				}
			}
			if((i_Seat_CurrentBidder==i_Seat_InitPlayer) && (i_Bid==0)){
				b_IsBiddingDone = true;
				i_MinNextBid = -1;
			}
			return true;
		}
	}
	private class CPlay29{
		private C29ClientS arr_clientS[];
		private int i_SeatCurrent; 
		public int GetCurrentPlayerSeat(){
			return i_SeatCurrent;
		}
		private int i_SuitTrump;  public int GetTrumpSuit(){return i_SuitTrump;}
		private int i_roundcount;
		private int i_SingleHandClientId; public int GetSingleHandClientId(){return i_SingleHandClientId;}
		public void SetSingleHandClientId(int id){
			i_SingleHandClientId = id;
			if(id > -1){
				i_SeatCurrent = arr_clientS[id].iSeatNo;
				o_trumpcard.SetSeventh(false);
			}
		}
		private CCard29 o_trumpcard;
		public void SetTrumpSuit(int iTrump){
			i_SuitTrump = iTrump;
			if(iTrump>-1 && iTrump <5)
				o_trumpcard = new CCard29(iTrump, 2);
		}
		public void SetSeventhCard(CCard29 cardSeventh){
			o_trumpcard = cardSeventh;
		}
		private	boolean b_isTrumpShown; public boolean GetIsTrumpShown(){return b_isTrumpShown;}
		private int i_TrumpShowHandCnt;
		private boolean b_isHandCompleted; public boolean GetIsHandCompleted(){return b_isHandCompleted;}
		private int arr_iSeat[];
		public void HandStarted()
		{
			b_isHandCompleted = false;
			i_handplaycnt = 0;
			i_TrumpShowHandCnt = -1;
		}
		private boolean b_isGameCompleted; public boolean GetIsGameCompleted(){return b_isGameCompleted;}
		private int i_handplaycnt; 
		private CCard29 arr_curtable[];
		private boolean b_ispairshown;
		private int i_bidchange; public int GetBidChange(){ return i_bidchange;}
		public CPlay29(C29Server server){
			arr_clientS = (C29ClientS [])server.arr_clientS;
			arr_iSeat = server.arr_iSeatList;
			b_isTrumpShown = false;
			i_SeatCurrent = server.i_nextplayerSeat;
			i_SuitTrump = -1;
			arr_curtable = new CCard29[4];
			b_isGameCompleted = false;
			o_trumpcard = null;
			i_roundcount = 0;
			b_ispairshown = false;
			i_bidchange = 0;
			i_SingleHandClientId = -2;
		}
		
//		private boolean DoHandHasSameSuit(LinkedList<CCard29> cardlist, CCard29 card){
//			ListIterator<CCard29> it = cardlist.listIterator();
//			while(it.hasNext())
//				if(it.next().GetSuit() == card.GetSuit())
//					return true;
//			return false;
//		}
		
		private boolean DoHandExceptSeventhHasSameSuit(LinkedList<CCard29> cardlist, CCard29 card){
			ListIterator<CCard29> it = cardlist.listIterator();
			while(it.hasNext()){
				CCard29 cardcur = it.next();
				if(cardcur.IsSeventh())
					continue;
				if(cardcur.GetSuit() == card.GetSuit())
					return true;
			}
			return false;
		}
		public CCard29 PlayShowTrump(int iClientid){
			if(this.i_SingleHandClientId > -1)
				return null;
			if(arr_clientS[iClientid].iSeatNo != i_SeatCurrent || i_handplaycnt==0 || b_isTrumpShown)
				return null;
			if(DoHandExceptSeventhHasSameSuit( ((C29ClientS)arr_clientS[iClientid]).GetCurrentCardList(), arr_curtable[0]))
				return null;
			b_isTrumpShown = true;
			i_TrumpShowHandCnt = i_handplaycnt;
			if(o_trumpcard.IsSeventh())
				o_trumpcard.SetSeventh(false);
			return o_trumpcard;
		}
		public boolean PlayCard(int iClientid, CCard29 Card){
			if(arr_clientS[iClientid].iSeatNo != i_SeatCurrent)
				return false;
			int cardindx = arr_clientS[iClientid].GetCurrentCardList().indexOf(Card);
			if(cardindx == -1)
				return false;
			if(Card.IsSeventh())
				return false;
			if((i_handplaycnt > 0) && (arr_curtable[0].GetSuit() != Card.GetSuit()) 
					&& DoHandExceptSeventhHasSameSuit( ((C29ClientS)arr_clientS[iClientid]).GetCurrentCardList(), arr_curtable[0]))
				return false;
			if((i_TrumpShowHandCnt == i_handplaycnt) && ( o_trumpcard.GetSuit()!=4)&& (Card.GetSuit() != o_trumpcard.GetSuit())){
				if(DoHandExceptSeventhHasSameSuit( ((C29ClientS)arr_clientS[iClientid]).GetCurrentCardList(), o_trumpcard))
					return false;
			}
			arr_curtable[i_handplaycnt] = ((C29ClientS)arr_clientS[iClientid]).GetCurrentCardList().remove(cardindx);
			i_handplaycnt = (i_handplaycnt + 1);
			i_SeatCurrent = (i_SeatCurrent + 1)%4;
			if((i_SingleHandClientId > -1) && ((arr_clientS[i_SingleHandClientId].iSeatNo + 2) % 4 == i_SeatCurrent))
				i_SeatCurrent = (i_SeatCurrent + 1)%4;
			
			if(i_SingleHandClientId == -1){
				if(i_handplaycnt == 4){
					b_isHandCompleted = true;
					i_roundcount++;
					if(i_roundcount == 8)
						this.b_isGameCompleted = true;
				}
			}
			else{
				if(i_handplaycnt == 3){
					b_isHandCompleted = true;
					i_roundcount++;
					if(i_roundcount == 8)
						this.b_isGameCompleted = true;
				}
			}
				
			return true;
		}
		public int DecideWinner(){
			if(!b_isHandCompleted)
				return -1;
			int ret = 0;
			if(i_SingleHandClientId > -1){
				for(int i =0; i<3;i++){
					if((arr_curtable[i].GetOrder() > arr_curtable[ret].GetOrder()) && (arr_curtable[i].GetSuit() == arr_curtable[0].GetSuit()))
						ret = i;
				}
				for(int i =0; i<3; i++)
					((C29ClientS)arr_clientS[arr_iSeat[(i_SeatCurrent+ret)%4]]).GetWonCardList().add(arr_curtable[i]);
				if(ret!=0 || this.b_isGameCompleted){
					arr_clientS[arr_iSeat[i_SeatCurrent]].GetWonCardList().addAll(arr_clientS[arr_iSeat[i_SeatCurrent]].GetCurrentCardList());
					arr_clientS[arr_iSeat[(i_SeatCurrent+1)%4]].GetWonCardList().addAll(arr_clientS[arr_iSeat[(i_SeatCurrent+1)%4]].GetCurrentCardList());
					arr_clientS[arr_iSeat[(i_SeatCurrent+3)%4]].GetWonCardList().addAll(arr_clientS[arr_iSeat[(i_SeatCurrent+3)%4]].GetCurrentCardList());
					arr_clientS[arr_iSeat[(i_SeatCurrent+2)%4]].GetWonCardList().addAll(arr_clientS[arr_iSeat[(i_SeatCurrent+2)%4]].GetCurrentCardList());
					this.b_isGameCompleted = true;
				}
				if(ret==2)
					return arr_iSeat[(i_SeatCurrent + ret + 1)%4];
				else
					return arr_iSeat[(i_SeatCurrent + ret)%4];
			}
			for(int i=0; i<4; i++){
				if(!b_isTrumpShown || ((arr_curtable[i].GetSuit() != o_trumpcard.GetSuit())
						&& (arr_curtable[ret].GetSuit() != o_trumpcard.GetSuit()))  || (i<i_TrumpShowHandCnt) || (arr_curtable[0].GetSuit() == o_trumpcard.GetSuit())){
					if((arr_curtable[i].GetOrder() > arr_curtable[ret].GetOrder()) && (arr_curtable[i].GetSuit() == arr_curtable[0].GetSuit()))
						ret = i;
				}
				else if((ret < i_TrumpShowHandCnt) && (arr_curtable[i].GetSuit() == o_trumpcard.GetSuit()))
					ret = i;
				else if(arr_curtable[ret].GetSuit() != o_trumpcard.GetSuit())
					ret = i;
				else if ((arr_curtable[ret].GetOrder() < arr_curtable[i].GetOrder())&&(arr_curtable[i].GetSuit() == o_trumpcard.GetSuit()))
					ret = i;
			}
			i_SeatCurrent = (i_SeatCurrent + ret)%4;
			for(int i =0; i<4; i++)
				((C29ClientS)arr_clientS[arr_iSeat[i_SeatCurrent]]).GetWonCardList().add(arr_curtable[i]);
			return arr_iSeat[i_SeatCurrent];
		}
		public boolean PlayPair(int iClientid){
			if(i_SingleHandClientId > -1)
				return false;
			if(arr_clientS[iClientid].iSeatNo != i_SeatCurrent || b_ispairshown)
				return false;
			if(!b_isTrumpShown || (i_TrumpShowHandCnt!=-1))
				return false;
			if(arr_clientS[iClientid].GetWonCardList().isEmpty() && arr_clientS[arr_iSeat[(arr_clientS[iClientid].iSeatNo + 2)%4]].GetWonCardList().isEmpty())
				return false;
			CCard29 o_King = new CCard29(o_trumpcard.GetSuit(),13);
			CCard29 o_Queen = new CCard29(o_trumpcard.GetSuit(),12);
			int indx_king = arr_clientS[iClientid].GetCurrentCardList().indexOf(o_King);
			int indx_queen = arr_clientS[iClientid].GetCurrentCardList().indexOf(o_Queen);
			if((indx_king == -1)||(indx_queen == -1))
				return false;
			if((arr_clientS[iClientid].iSeatNo - bid.i_Seat_CurrentWinner)%2 == 0){
				if(bid.i_currentWinningBid >=20)
					this.i_bidchange = -4;
			}
			else
				this.i_bidchange = 4;
			return true;
		}
	}
	private CDeck29 deck;
	private CBid29 bid;
	private int i_nextplayerSeat;
	private boolean b_isTrumpsetted;
	private int i_DoubleRedouble;
	private CGamePointInfo o_gamePoint;

	private CPlay29 o_play;
	//private int i_SingleHandClientId;
	public C29Server(int port){
		super(port, true);
		arr_clientS = new C29ClientS[i_totplayer];
		for(int i=0; i<i_totplayer; i++)
			arr_clientS[i] = new C29ClientS();
		deck = new CDeck29();
		i_nextplayerSeat = 0;	
		o_gamePoint = new CGamePointInfo(); 
	}
	private void ResetGameState(){
		b_isTrumpsetted = false;
		i_DoubleRedouble = -1;
		for(int i=0; i<4; i++){
			((C29ClientS)arr_clientS[i]).SetReady2Type(-1);
			((C29ClientS)arr_clientS[i]).SetReady3Type(-1);
			((C29ClientS)arr_clientS[i]).GetWonCardList().clear();
		}
		o_play = new CPlay29(this);
		deck.BiasedShuffle();

	}
	
	public void Init() throws InterruptedException{
		super.Init();
		int i_finish = 0;
		while(i_finish==0 && this.b_running){
			WaitForEverybodyTobeReady();
			System.out.println("C29Server::Init::Everybody is Ready Init Done");
			ResetGameState();
			e_gamestate = GameState29.BIDDING;
			WaitForBiddingDone();
			if(bid!=null)
				System.out.println("C29Server::Init::Bidding Completed. Winner Seat No " + bid.i_Seat_CurrentWinner + ". Winning Bid " + bid.i_currentWinningBid);
			e_gamestate = GameState29.SETTING_TRUMP;
			WaitForTrumpSetting();
			e_gamestate = GameState29.WAITING_4_OK2;
			WaitForDoubleRedoubleDone();
			e_gamestate = GameState29.WAITING_4_OK3;
			System.out.println("C29Server::Init::Double:Redouble Done. DoubleRedouble state " + this.i_DoubleRedouble);
			WaitForSingleHandCallDone();
			if(o_play!=null)
				System.out.println("C29Server::Init:: Decided on Single Hand, Single Hand ClientId " + o_play.GetSingleHandClientId());
			e_gamestate = GameState29.PLAYING;
			WaitForPlayCompletionDone();
			e_gamestate = GameState29.PLAY_FIN;
			CGameCompletionInfo o_result = GetGameResult();
			if(o_result!=null && o_result.winner!=-1){
				if(o_play.GetSingleHandClientId() > -1)
					i_finish = o_gamePoint.UpdatePoint(o_result.winner, ((C29ClientS)arr_clientS[o_play.GetSingleHandClientId()]).iSeatNo, this.i_DoubleRedouble, true);
				else
					i_finish = o_gamePoint.UpdatePoint(o_result.winner, bid.i_Seat_CurrentWinner, this.i_DoubleRedouble, false);
			}
			NextJobTypes[] arr_nextJobTypes = new NextJobTypes[4];
			for(int i =0; i<4; i++){
				arr_nextJobTypes[i] = NextJobTypes.WAIT;
				((C29ClientS)arr_clientS[i]).setNextjob(arr_nextJobTypes[i]);
			}
			CMessage29 snd = CMessage29.CreateMsg_GameCompletionInfo_Serv(-1, -1, arr_nextJobTypes, o_result, o_gamePoint);
			this.BroadCast(snd);
			i_nextplayerSeat = (i_nextplayerSeat+1)%4;
			if(o_play!=null && o_play.GetSingleHandClientId() > -1)
				i_nextplayerSeat = (((C29ClientS)arr_clientS[o_play.GetSingleHandClientId()]).iSeatNo + 1)%4;
		}
	}
	
	private CGameCompletionInfo GetGameResult(){
		if(!b_running)
			return null;
		CGameCompletionInfo ret;
		ret = new CGameCompletionInfo();
		ret.list_listwincards = new LinkedList<LinkedList<CCard29>>();
		ret.arr_points = new int[4];
		int arr_cardcount[] = new int[4];
		for(int i=0; i<4;i++){
			arr_cardcount[i] = ((C29ClientS)arr_clientS[i]).GetWonCardList().size();
			ret.list_listwincards.add(((C29ClientS)arr_clientS[i]).GetWonCardList());
			deck.list_cardsCurrent.addAll(((C29ClientS)arr_clientS[i]).GetWonCardList());
			ret.arr_points[i] = 0;
			ListIterator<CCard29> it = ((C29ClientS)arr_clientS[i]).GetWonCardList().listIterator();
			while(it.hasNext())
				ret.arr_points[i]+= it.next().GetPoint();
			if(i == this.arr_iSeatList[o_play.GetCurrentPlayerSeat()])
				ret.arr_points[i]+=1;
		}
		if(o_play.GetSingleHandClientId() > -1){
			boolean b_singlehandwin = true;
			if(arr_cardcount[o_play.GetSingleHandClientId()] != 24)
				b_singlehandwin = false;
			if(((C29ClientS)arr_clientS[o_play.GetSingleHandClientId()]).iSeatNo %2 ==0){
				if(b_singlehandwin)
					ret.winner = 0;
				else
					ret.winner = 1;
			}
			else{
				if(b_singlehandwin)
					ret.winner = 1;
				else
					ret.winner = 0;
			}
			return ret;
		}
		if((bid.i_Seat_CurrentWinner % 2) ==0){
			if(ret.arr_points[arr_iSeatList[0]] + ret.arr_points[arr_iSeatList[2]] >= bid.i_currentWinningBid + o_play.GetBidChange()){
				if((arr_cardcount[arr_iSeatList[1]] + arr_cardcount[arr_iSeatList[3]]) == 0)
					ret.winner = 2;
				else
					ret.winner = 0;
			}
			else if(ret.arr_points[arr_iSeatList[0]] + ret.arr_points[arr_iSeatList[2]] < (bid.i_currentWinningBid + o_play.GetBidChange() + 1)/2)
				ret.winner = 3;
			else
				ret.winner = 1;
		}
		else{
			if(ret.arr_points[arr_iSeatList[1]] + ret.arr_points[arr_iSeatList[3]] >= bid.i_currentWinningBid + o_play.GetBidChange()){
				if((arr_cardcount[arr_iSeatList[0]] + arr_cardcount[arr_iSeatList[2]]) == 0)
					ret.winner = 3;
				else
					ret.winner = 1;
			}
			else if(ret.arr_points[arr_iSeatList[1]] + ret.arr_points[arr_iSeatList[3]] < (bid.i_currentWinningBid + o_play.GetBidChange() + 1)/2)
				ret.winner = 2;
			else
				ret.winner = 0;
		}
			
		if(!o_play.GetIsTrumpShown())
			ret.winner = -1;
		return ret;
	}
	
	private void WaitForPlayCompletionDone() throws InterruptedException{
		if(!b_running)
			return;
		do{
			o_play.HandStarted();
			NextJobTypes[] arr_nextJobTypes = new NextJobTypes[4];
			for(int i =0; i<4; i++){
				if(i==this.arr_iSeatList[o_play.GetCurrentPlayerSeat()])
					arr_nextJobTypes[i] = NextJobTypes.PLAY;
				else
					arr_nextJobTypes[i] = NextJobTypes.WAIT;
				((C29ClientS)arr_clientS[i]).setNextjob(arr_nextJobTypes[i]);
			}
			CMessage29 snd = CMessage29.CreateMsg_StartPlay_Serv(-1, -1, arr_nextJobTypes);
			this.BroadCast(snd);
			while(!o_play.GetIsHandCompleted() && b_running){
				synchronized(this){
					this.wait();
				}
			}
			int i_winnerid = o_play.DecideWinner();
			NextJobTypes[] arr_nextjobs2 = new NextJobTypes[4];
			for(int i=0; i<4; i++)
				arr_nextjobs2[i] = NextJobTypes.WAIT;
			CMessage29 snd2 = CMessage29.CreateMsg_HandWinnerInfo_Serv(-1, -1, i_winnerid, arr_nextjobs2);
			this.BroadCast(snd2);
			Thread.sleep(1000);
		}while(!o_play.GetIsGameCompleted() && b_running);
		return;
	}
	private void WaitForSingleHandCallDone() throws InterruptedException{
		if(!b_running)
			return;
		NextJobTypes[] arr_nextJobTypes = new NextJobTypes[4];
		for(int i=0; i<4; i++){
			arr_nextJobTypes[i] = NextJobTypes.GET_READY3;
			((C29ClientS)arr_clientS[i]).setNextjob(arr_nextJobTypes[i]);
		}
		for(int j=0; j<4; j++){
			int iSeatNo = (i_nextplayerSeat + j)%4;
			CCard29[] arr_fourcards = new CCard29[4];
			for(int i = 0; i<4; i++){
				arr_fourcards[i] = deck.GetNextCard();
				if(iSeatNo == bid.i_Seat_CurrentWinner && o_play.GetTrumpSuit() == 5 && i==3){
					arr_fourcards[i].SetSeventh(true);
					o_play.SetSeventhCard(arr_fourcards[i]);
				}
				((C29ClientS)arr_clientS[arr_iSeatList[iSeatNo]]).GetCurrentCardList().add(arr_fourcards[i]);
			}
			CMessage29 snd = CMessage29.CreateMsg_FourCards2_Serv(arr_fourcards, arr_iSeatList[iSeatNo],
					arr_clientS[arr_iSeatList[iSeatNo]].i_outmsg_cnt++, arr_nextJobTypes);
			arr_clientS[arr_iSeatList[iSeatNo]].q_msg_out.add(snd);
		}
		while(o_play.GetSingleHandClientId()== -2 && b_running){
			synchronized(this){
				this.wait();
			}
		}
	}
	private void WaitForDoubleRedoubleDone() throws InterruptedException{
		if(!b_running)
			return;
		NextJobTypes[] arr_nextJobTypes = new NextJobTypes[4];
		for(int iSeatNo = 0; iSeatNo<4; iSeatNo++){
			if((bid.i_Seat_CurrentWinner - iSeatNo)%2==0)
				arr_nextJobTypes[this.arr_iSeatList[iSeatNo]] = NextJobTypes.WAIT;
			else
				arr_nextJobTypes[this.arr_iSeatList[iSeatNo]] = NextJobTypes.GET_READY2;
			((C29ClientS)arr_clientS[this.arr_iSeatList[iSeatNo]]).setNextjob(arr_nextJobTypes[this.arr_iSeatList[iSeatNo]]);
		}
		boolean isSeventh = false;
		if(o_play.GetTrumpSuit() == 5)
			isSeventh = true;
		
		CMessage29 snd = CMessage29.CreateMsg_TrumpSetDone_Serv(-1, -1, arr_nextJobTypes, this.arr_iSeatList[bid.i_Seat_CurrentWinner], isSeventh);
		BroadCast(snd);
		while(this.i_DoubleRedouble == -1 && b_running){
			synchronized(this){
				this.wait();
			}
		}
	}
	private void WaitForTrumpSetting() throws InterruptedException{
		if(!b_running)
			return;
		NextJobTypes[] arr_nextJobTypes = new NextJobTypes[4];
		for(int iSeatNo = 0; iSeatNo<4; iSeatNo++){
			if(iSeatNo==bid.i_Seat_CurrentWinner)
				arr_nextJobTypes[this.arr_iSeatList[iSeatNo]] = NextJobTypes.SET_TRUMP;
			else
				arr_nextJobTypes[this.arr_iSeatList[iSeatNo]] = NextJobTypes.WAIT;
			((C29ClientS)arr_clientS[this.arr_iSeatList[iSeatNo]]).setNextjob(arr_nextJobTypes[this.arr_iSeatList[iSeatNo]]);
		}
		CMessage29 snd = CMessage29.CreateMsg_BidCompletionInfo_Serv(-1, -1, arr_nextJobTypes,this.arr_iSeatList[bid.i_Seat_CurrentWinner], bid.i_currentWinningBid);
		this.BroadCast(snd);
		while(!b_isTrumpsetted && b_running){
			synchronized(this){
				this.wait();
			}
		}
		return;
		
	}
	@Override
	protected void ProcessMsg(Object obj){
		try{
			CMessage29 msg = (CMessage29)obj;
			if(msg.e_type!=MsgType.CLIENTMSG){
				System.err.println("Not valid Message Type ");
				return;
			}
			switch(msg.e_datatype29){
			case JOB_DONE_BID:
				if(e_gamestate!=GameState29.BIDDING)
					break;
				if(((C29ClientS)arr_clientS[msg.i_clientid]).getNextjob() != NextJobTypes.BID)
					break;
				int iClientId = msg.i_clientid;
				int iBid = ((Integer)msg.arr_data[0]).intValue();
				if(!bid.BidReceived(((C29ClientS)arr_clientS[iClientId]).iSeatNo, iBid)){
					System.err.println("C29Server::ProcessMsg::Bid Not accepted Treating as pass");
					bid.BidReceived(((C29ClientS)arr_clientS[iClientId]).iSeatNo, 0);
					SomeOneBidded(iClientId,0);
					break;
				}
				SomeOneBidded(iClientId,iBid);
				break;
			case JOB_DONE_SET_TRUMP:
				if(e_gamestate!=GameState29.SETTING_TRUMP)
					break;
				if(((C29ClientS)arr_clientS[msg.i_clientid]).getNextjob() != NextJobTypes.SET_TRUMP)
					break;
				TrumpSetDone(msg.i_clientid, ((Integer)msg.arr_data[0]).intValue());
				break;
			case JOB_DONE_GET_READY2:
				if(e_gamestate!=GameState29.WAITING_4_OK2)
					break;
				if(((C29ClientS)arr_clientS[msg.i_clientid]).getNextjob() != NextJobTypes.GET_READY2)
					break;
				SomeOneGotReady2(msg.i_clientid, ((Integer)msg.arr_data[0]).intValue());
				break;
			case JOB_DONE_GET_READY3:
				if(e_gamestate!=GameState29.WAITING_4_OK3)
					break;
				if(((C29ClientS)arr_clientS[msg.i_clientid]).getNextjob() != NextJobTypes.GET_READY3)
					break;
				SomeOneGotReady3(msg.i_clientid, ((Integer)msg.arr_data[0]).intValue());
				break;
			case JOB_DONE_PLAY_CARD:
				if(e_gamestate!=GameState29.PLAYING)
					break;
				if(((C29ClientS)arr_clientS[msg.i_clientid]).getNextjob() != NextJobTypes.PLAY)
					break;
				SomeOnePlayedCard(msg.i_clientid,(CCard29)msg.arr_data[0]);
				break;
			case JOB_DONE_PLAY_SHOWTRUMP:
				if(e_gamestate!=GameState29.PLAYING)
					break;
				if(((C29ClientS)arr_clientS[msg.i_clientid]).getNextjob() != NextJobTypes.PLAY)
					break;
				SomeOneCalledShowTrump(msg.i_clientid);
			case JOB_DONE_PLAY_PAIR:
				if(e_gamestate!=GameState29.PLAYING)
					break;
				if(((C29ClientS)arr_clientS[msg.i_clientid]).getNextjob() != NextJobTypes.PLAY)
					break;
				SomeOneCalledPair(msg.i_clientid);
				break;
			default:
				System.err.println("C29Server::ProcessMsg::Messagetype Not Supported");
			}
		}
		catch(ClassCastException e){
			super.ProcessMsg(obj);
			return;
		}

	}
	
	private void SomeOneCalledPair(int iClientId){
		NextJobTypes[] arr_nextJobTypes;
		arr_nextJobTypes = new NextJobTypes[4];
		CMessage29 msg;

		for(int i =0; i<4; i++)
				arr_nextJobTypes[i] = ((C29ClientS)arr_clientS[i]).getNextjob();
		if(!o_play.PlayPair(iClientId))
			msg = CMessage29.CreateMsg_JobDoneBadPlay_Serv(-1, -1, iClientId, arr_nextJobTypes);
		else
			msg = CMessage29.CreateMsg_JobDonePair_Serv(-1, -1, arr_nextJobTypes, iClientId, o_play.GetBidChange());
		this.BroadCast(msg);
		return;
	}
	
	private void SomeOneCalledShowTrump(int iClientId){
		NextJobTypes[] arr_nextJobTypes;
		arr_nextJobTypes = new NextJobTypes[4];
		CMessage29 msg;
		for(int i =0; i<4; i++)
				arr_nextJobTypes[i] = ((C29ClientS)arr_clientS[i]).getNextjob();	
		CCard29 trump = o_play.PlayShowTrump(iClientId);
		if(trump == null)
			msg = CMessage29.CreateMsg_JobDoneBadPlay_Serv(-1, -1, iClientId, arr_nextJobTypes);
		else
			msg = CMessage29.CreateMsg_JobDonePlayShowTrump_Serv(-1, -1, arr_nextJobTypes, iClientId, trump);
		this.BroadCast(msg);
		return;
			
	}
	private void SomeOnePlayedCard(int iClientId, CCard29 card){
		if(!o_play.PlayCard(iClientId, card)){
			NextJobTypes[] arr_nextJobTypes;
			arr_nextJobTypes = new NextJobTypes[4];
			for(int i =0; i<4; i++)
					arr_nextJobTypes[i] = ((C29ClientS)arr_clientS[i]).getNextjob();			
			CMessage29 msg = CMessage29.CreateMsg_JobDoneBadPlay_Serv(-1, -1, iClientId, arr_nextJobTypes);
			this.BroadCast(msg);
			return;
		}
		
		NextJobTypes[] arr_nextJobTypes;
		arr_nextJobTypes = new NextJobTypes[4];
		for(int i =0; i<4; i++){
			if(i==this.arr_iSeatList[o_play.GetCurrentPlayerSeat()] && !o_play.GetIsHandCompleted())
				arr_nextJobTypes[i] = NextJobTypes.PLAY;
			else
				arr_nextJobTypes[i] = NextJobTypes.WAIT;
			((C29ClientS)arr_clientS[i]).setNextjob(arr_nextJobTypes[i]);
		}
		CMessage29 msg = CMessage29.CreateMsg_JobDonePlayCard_Serv(-1, -1, arr_nextJobTypes, iClientId, card);
		this.BroadCast(msg);
		
		if(o_play.GetIsHandCompleted()){
			HandCompleted();
		}
		return;
	}
	
	private void HandCompleted(){
		synchronized(this){
			this.notifyAll();
		}
	}
 	private void SomeOneGotReady3(int iClientId, int iReadyType){
		if(((C29ClientS)arr_clientS[iClientId]).GetReady3Type()!=-1)
			return;
		if(iReadyType!=0 && iReadyType!=1){
			System.err.println("C29Server::SomeOneGotReady3:: Invalid Ready Type " + iReadyType);
			return;
		}
		NextJobTypes[] arr_nextJobTypes;
		arr_nextJobTypes = new NextJobTypes[4];
		((C29ClientS)arr_clientS[iClientId]).SetReady3Type(iReadyType);
		for(int i=0; i<4; i++){			
			if(iReadyType == 0){
				if(i == iClientId)
					arr_nextJobTypes[i] = NextJobTypes.WAIT;
				else
					arr_nextJobTypes[i] = ((C29ClientS)arr_clientS[i]).getNextjob();
			}
			else{
				if(i!=iClientId)
					((C29ClientS)arr_clientS[i]).SetReady3Type(0);
				arr_nextJobTypes[i] = NextJobTypes.WAIT;
			}
			((C29ClientS)arr_clientS[i]).setNextjob(arr_nextJobTypes[i]);
		}
		CMessage29 snd = CMessage29.CreateMsg_JobDoneGetReady3_Serve(-1, -1, arr_nextJobTypes, iClientId, iReadyType);
		this.BroadCast(snd);
		boolean b_everybodyready = true;
		for(int i =0; i<4; i++)
			if(((C29ClientS)arr_clientS[i]).GetReady3Type() == -1){
				b_everybodyready = false;
				break;
			}
		if(b_everybodyready)		
			EveryBodyIsReady3();
		return;
	}
	
	private void EveryBodyIsReady3(){
		o_play.SetSingleHandClientId(-1);
		for(int i=0; i<4; i++)
			if(((C29ClientS)arr_clientS[i]).GetReady3Type() == 1){
				o_play.SetSingleHandClientId(i);
				break;
			}
		synchronized(this){
			this.notifyAll();
		}
		return;
	}
	private boolean SomeOneGotReady2Helper0(int iClientId, int iReadyType, NextJobTypes[] arr_nextJobTypes){
		if(iReadyType !=0 && iReadyType!=1){
			System.err.println("C29Server::SomeOneGotReady2Helper0:: Redouble not allowed need to give double. Ready Type " + iReadyType);
			return false;
		}
		if(((C29ClientS)arr_clientS[iClientId]).GetReady2Type()!=-1)
			return false;
		((C29ClientS)arr_clientS[iClientId]).SetReady2Type(iReadyType);
		
		if(((C29ClientS)arr_clientS[arr_iSeatList[(((C29ClientS)GetClients()[iClientId]).iSeatNo+2)%4]]).GetReady2Type()==-1){
			if(iReadyType == 0)
				for(int i=0; i<4;i++){
					if(i!=iClientId)
						arr_nextJobTypes[i] = ((C29ClientS)GetClients()[i]).getNextjob();
					else
						arr_nextJobTypes[i] = NextJobTypes.WAIT;
					((C29ClientS)GetClients()[i]).setNextjob(arr_nextJobTypes[i]);
				}
			else{
				for(int i = 0; i<4; i++){
					if(i==arr_iSeatList[(((C29ClientS)GetClients()[iClientId]).iSeatNo+2)%4]){//If somebody gives double that means his partner is Ready
						((C29ClientS)arr_clientS[i]).SetReady2Type(0);
						arr_nextJobTypes[i] = NextJobTypes.WAIT;
					}
					else if(i==iClientId)
						arr_nextJobTypes[i] = NextJobTypes.WAIT;
					else
						arr_nextJobTypes[i] = NextJobTypes.GET_READY2;
					((C29ClientS)GetClients()[i]).setNextjob(arr_nextJobTypes[i]);
				}
			}
		}
		else{
			if(iReadyType!=0){
				for(int i = 0; i<4; i++){
					if(i == iClientId || i == arr_iSeatList[(((C29ClientS)GetClients()[iClientId]).iSeatNo + 2)%4])
						arr_nextJobTypes[i] = NextJobTypes.WAIT;
					else
						arr_nextJobTypes[i] = NextJobTypes.GET_READY2;
					((C29ClientS)GetClients()[i]).setNextjob(arr_nextJobTypes[i]);
				}	
			}
			else{
				for(int i=0; i<4; i++){
					((C29ClientS)arr_clientS[i]).SetReady2Type(0);
					arr_nextJobTypes[i] = NextJobTypes.WAIT;
					((C29ClientS)GetClients()[i]).setNextjob(arr_nextJobTypes[i]);
				}
			}
		}

		return true;
	}
	
	private boolean SomeOneGotReady2Helper1(int iClientId, int iReadyType, NextJobTypes[] arr_nextJobTypes){
		if(iReadyType !=0 && iReadyType!=2){
			System.err.println("C29Server::SomeOneGotReady2Helper1:: Double not allowed need to give Redouble. Ready Type " + iReadyType);
			return false;
		}
		
		if(((C29ClientS)arr_clientS[iClientId]).GetReady2Type()!=-1)
			return false;
		
		((C29ClientS)arr_clientS[iClientId]).SetReady2Type(iReadyType);
		if(((C29ClientS)arr_clientS[arr_iSeatList[(((C29ClientS)GetClients()[iClientId]).iSeatNo+2)%4]]).GetReady2Type()==-1){
			if(iReadyType == 0)
				for(int i=0; i<4;i++){
					if(i!=iClientId)
						arr_nextJobTypes[i] = ((C29ClientS)GetClients()[i]).getNextjob();
					else
						arr_nextJobTypes[i] = NextJobTypes.WAIT;
					((C29ClientS)GetClients()[i]).setNextjob(arr_nextJobTypes[i]);
				}
			else{
				for(int i = 0; i<4; i++){
					if(i==arr_iSeatList[(((C29ClientS)GetClients()[iClientId]).iSeatNo+2)%4]){//If somebody gives redouble that means his partner is Ready
						((C29ClientS)arr_clientS[i]).SetReady2Type(0);
						arr_nextJobTypes[i] = NextJobTypes.WAIT;
					}
					else
						arr_nextJobTypes[i] = NextJobTypes.WAIT;
					((C29ClientS)GetClients()[i]).setNextjob(arr_nextJobTypes[i]);
				}
			}
		}
		else{
			for(int i=0; i<4; i++){
				arr_nextJobTypes[i] = NextJobTypes.WAIT;
				((C29ClientS)GetClients()[i]).setNextjob(arr_nextJobTypes[i]);
			}
		}
		return true;
	}
	private void SomeOneGotReady2(int iClientId, int iReadyType){
		NextJobTypes[] arr_nextJobTypes;
		arr_nextJobTypes = new NextJobTypes[4];
		if((((C29ClientS)GetClients()[iClientId]).iSeatNo - bid.i_Seat_CurrentWinner) %2 !=0 ) {
			if(!SomeOneGotReady2Helper0(iClientId, iReadyType,arr_nextJobTypes))
				return;
		}
		else{
			if(!SomeOneGotReady2Helper1(iClientId, iReadyType,arr_nextJobTypes))
				return;
		}
		CMessage29 msg = CMessage29.CreateMsg_JobDoneGetReady2_Serve(-1, -1, arr_nextJobTypes, iClientId, iReadyType);
		BroadCast(msg);
		boolean b_isEverybodyready2 = true;
		for(int i=0; i<4; i++)
			if(((C29ClientS)arr_clientS[i]).GetReady2Type()==-1){
				b_isEverybodyready2 = false;
				break;
			}
		if(b_isEverybodyready2)
			EveryBodyIsReady2();
		return;
	}
	
	private void EveryBodyIsReady2(){
		for(int i=0; i<4; i++){
			if(((C29ClientS)arr_clientS[i]).GetReady2Type()==1)
				this.i_DoubleRedouble = 1;
			if(((C29ClientS)arr_clientS[i]).GetReady2Type() == 2){
				this.i_DoubleRedouble = 2;
				break;
			}	
		}
		if(i_DoubleRedouble < 0)
			i_DoubleRedouble = 0;
		synchronized(this){
			this.notifyAll();
		}
		return;
	}
	private void TrumpSetDone(int iClientId, int iTrump){
		o_play.SetTrumpSuit(iTrump);
		b_isTrumpsetted = true;
		synchronized(this){
			this.notifyAll();
		}
		return;
	}
	private void SomeOneBidded(int iClientId, int iBid){
		NextJobTypes[] arr_nextJobTypes = new NextJobTypes[4];
		for(int iSeatNo = 0; iSeatNo<4; iSeatNo++){
			if((iSeatNo==bid.i_Seat_CurrentBidder) && !bid.b_IsBiddingDone)
				arr_nextJobTypes[this.arr_iSeatList[iSeatNo]] = NextJobTypes.BID;
			else
				arr_nextJobTypes[this.arr_iSeatList[iSeatNo]] = NextJobTypes.WAIT;
			((C29ClientS)arr_clientS[this.arr_iSeatList[iSeatNo]]).setNextjob(arr_nextJobTypes[this.arr_iSeatList[iSeatNo]]);
		}
		CMessage29 snd = CMessage29.CreateMsg_BidUpdated_Serv(-1,-1, arr_nextJobTypes,iClientId,iBid,bid.i_MinNextBid);
		this.BroadCast(snd);
		if(bid.b_IsBiddingDone){
			synchronized(this){
				this.notifyAll();
			}
		}	
		
	}
	
	private void WaitForBiddingDone() throws InterruptedException{
		if(!b_running)
			return;
		do{
			bid = new CBid29(i_nextplayerSeat);
			NextJobTypes[] arr_nextJobTypes = new NextJobTypes[4];
			for(int iSeatNo = 0; iSeatNo<4; iSeatNo++){
				if(iSeatNo==bid.i_Seat_CurrentBidder)
					arr_nextJobTypes[this.arr_iSeatList[iSeatNo]] = NextJobTypes.BID;
				else
					arr_nextJobTypes[this.arr_iSeatList[iSeatNo]] = NextJobTypes.WAIT;
				((C29ClientS)arr_clientS[this.arr_iSeatList[iSeatNo]]).setNextjob(arr_nextJobTypes[this.arr_iSeatList[iSeatNo]]);
			}
			
			for(int j=0; j<4; j++){
				int iSeatNo = (i_nextplayerSeat + j)%4;
				//((C29ClientS)arr_clientS[arr_iSeatList[iSeatNo]]).GetCurrentCardList().clear();
				CCard29[] arr_fourcards = new CCard29[4];
				for(int i = 0; i<4; i++){
					arr_fourcards[i] = deck.GetNextCard();
					((C29ClientS)arr_clientS[arr_iSeatList[iSeatNo]]).GetCurrentCardList().add(arr_fourcards[i]);
				}
				CMessage29 snd = CMessage29.CreateMsg_Deal4Cards_Serv(arr_fourcards, arr_iSeatList[iSeatNo],
						arr_clientS[arr_iSeatList[iSeatNo]].i_outmsg_cnt++, arr_nextJobTypes,bid.i_MinNextBid);
				arr_clientS[arr_iSeatList[iSeatNo]].q_msg_out.add(snd);
			}
			while(!this.bid.b_IsBiddingDone && b_running){
				synchronized(this){
					this.wait();
				}
			}		
			if(bid.i_Seat_CurrentWinner == -1){
				for(int i=0; i<4; i++){
					deck.list_cardsCurrent.addAll(((C29ClientS)arr_clientS[arr_iSeatList[i]]).GetCurrentCardList());
					((C29ClientS)arr_clientS[arr_iSeatList[i]]).GetCurrentCardList().clear();
				}
				Collections.shuffle(deck.list_cardsCurrent);
			}
		}while(bid.i_Seat_CurrentWinner==-1 && b_running);
		return;
	}
}
